/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.autoupdate; import java.io.*; import java.security.*; import java.security.cert.*; import java.security.cert.Certificate; import java.util.*; import java.util.jar.*; import java.util.zip.*; import java.text.MessageFormat; import org.openide.util.NbBundle; import org.openide.util.RequestProcessor; /** Class for verifying signs in NBM Files. * @author Martin Ryzl, Petr Hrebejk * @version */ class SignVerifier extends Object { /** Resource bundle */ private static final ResourceBundle bundle = NbBundle.getBundle( SignVerifier.class ); /** The password to the Forte keystore */ private static final String KS_PSSWD = "open4all"; // NOI18N public static final int NOT_CHECKED = -1; public static final int BAD_DOWNLOAD = 0; public static final int CORRUPTED = 1; public static final int NOT_SIGNED = 2; public static final int SIGNED = 3; public static final int TRUSTED = 4; private static final String NEW_LINE = "\n"; // NOI18N private static final String SPACE = " "; // NOI18N private static final String TAB = "\t"; //NOI18N /** The collection of modules for update */ private Updates updates; /** The update check progress panel */ ProgressDialog progressDialog; /** Wizard validator, enables the Next button in wizard */ private Wizard.Validator validator; /** Total size of the verify */ private int verifySize; /** KBytes verified */ private long totalVerified; /** Number of modules to verify */ private long modulesCount; private boolean verifyCanceled = false; /** Creates new VeriSign */ SignVerifier( Updates updates, ProgressDialog progressDialog, Wizard.Validator validator ) { this.updates = updates; this.validator = validator; this.progressDialog = progressDialog; } /** Verifies all downloaded modules */ void doVerify() { verifyCanceled = false; Runnable task = new Runnable () { public void run() { progressDialog.setLabelText( ProgressDialog.PARTIAL_LABEL, bundle.getString( "CTL_PreparingVerify_Label" ) ); verifySize = getTotalVerifySize(); if ( verifyCanceled ) return; verifyAll(); if ( verifyCanceled ) return; validator.setValid( true ); } }; RequestProcessor.postRequest( task ); } /** Total size for verify in KBytes */ int getTotalVerifySize( ) { long result = 0L; modulesCount = 0; Iterator it = updates.getModules().iterator(); while( it.hasNext() ) { ModuleUpdate mu = (ModuleUpdate)it.next(); if ( mu.isSelected() && mu.isDownloadOK() && mu.getSecurity() == NOT_CHECKED ) { File file = Downloader.getNBM( mu ); result += file.length(); modulesCount++; } } return (int)(result / 1024); } void verifyAll() { progressDialog.setGaugeBounds( ProgressDialog.OVERALL_GAUGE, 0, verifySize ); progressDialog.setGaugeValue( ProgressDialog.OVERALL_GAUGE, 0 ); progressDialog.setLabelText( ProgressDialog.OVERALL_LABEL, "" ); // NOI18N progressDialog.setGaugeValue( ProgressDialog.PARTIAL_GAUGE, 0 ); int currentModule = 0; totalVerified = 0; Iterator it = updates.getModules().iterator(); while( it.hasNext() ) { if ( verifyCanceled ) return; ModuleUpdate mu = (ModuleUpdate)it.next(); if ( mu.isSelected() && mu.isDownloadOK() && mu.getSecurity() == NOT_CHECKED ) { if ( verifyCanceled ) return; progressDialog.setGaugeValue( ProgressDialog.PARTIAL_GAUGE, 0 ); progressDialog.setLabelText( ProgressDialog.PARTIAL_LABEL, mu.getName() + " [" + (currentModule + 1) + "/" + modulesCount + "]" ); //NOI18N File file = Downloader.getNBM( mu ); try { Collection certificates = verifyJar( file ); //showCollection(certificates); if ( certificates == null ) { mu.setSecurity( NOT_SIGNED ); mu.setInstallApproved( false ); } else { mu.setCerts( certificates ); if ( isTrusted( certificates ) ) { mu.setSecurity( TRUSTED ); mu.setInstallApproved( true ); } else { mu.setSecurity( SIGNED ); mu.setInstallApproved( false ); } } } catch( SecurityException e ) { mu.setSecurity( CORRUPTED ); mu.setInstallApproved( false ); } catch( IOException e ) { mu.setSecurity( BAD_DOWNLOAD ); mu.setInstallApproved( false ); mu.setDownloadOK( false ); } currentModule++; } } progressDialog.setGaugeValue( ProgressDialog.OVERALL_GAUGE, verifySize ); String mssgTotal = MessageFormat.format( bundle.getString( "FMT_VerifiedTotal" ), new Object[] { new Integer( verifySize ), new Integer( verifySize ) } ); progressDialog.setLabelText( ProgressDialog.OVERALL_LABEL, mssgTotal ); } /** * @param args the command line arguments */ /* public void check ( File file ) throws Exception { Collection certificates = verifyJar( file ); showCollection(certificates); KeyStore keyStore = getKeyStore(KEYSTORE, "changeit", null); showCollection(getCertificates(keyStore)); } */ /** Verify jar file. * @return Collection of certificates */ public Collection verifyJar( File jarName ) throws SecurityException, IOException { JarInputStream jis = null; boolean anySigned = false; boolean anyUnsigned = false; int moduleVerified = 0; int flen = (int) jarName.length(); progressDialog.setGaugeBounds( ProgressDialog.PARTIAL_GAUGE, 0, flen / 1024 ); List entries = new LinkedList(); jis = new JarInputStream(new FileInputStream(jarName)); ZipEntry entry; byte[] buffer = new byte[8192]; while ((entry = jis.getNextEntry()) != null) { progressDialog.setGaugeValue( ProgressDialog.PARTIAL_GAUGE, moduleVerified ); entries.add(entry); int len; try { while((len = jis.read(buffer)) != -1) { // we just read. ( and report progress // jis will throw a SecurityException // if a signature/digest check fails. // moduleVerified += len; // totalVerified += len; //System.out.println("MV " + (moduleVerified /1024) + "/" + ( flen / 1024 ) + " TV " + (totalVerified / 1024)); // NOI18N if ( verifyCanceled ) return null; } } finally { totalVerified += entry.getCompressedSize(); moduleVerified += entry.getCompressedSize(); String mssgTotal = MessageFormat.format( bundle.getString( "FMT_VerifiedTotal" ), new Object[] { new Integer( (int)(totalVerified / 1024) ), new Integer( verifySize ) } ); progressDialog.setGaugeValue( ProgressDialog.OVERALL_GAUGE, (int)(totalVerified / 1024) > verifySize ? verifySize : (int)( totalVerified / 1024 ) ); progressDialog.setLabelText( ProgressDialog.OVERALL_LABEL, mssgTotal ); progressDialog.setGaugeValue( ProgressDialog.PARTIAL_GAUGE, moduleVerified / 1024 ); } } jis.close(); if ( verifyCanceled ) return null; Manifest man = jis.getManifest(); Set certificates = new HashSet(); if (man != null) { Iterator e = entries.iterator(); while (e.hasNext()) { JarEntry je = (JarEntry) e.next(); String name = je.getName(); Certificate[] certs = je.getCertificates(); boolean isSigned = ((certs != null) && (certs.length > 0)); anySigned |= isSigned; if (certs != null) { for(int i = 0; i < certs.length; i++) { certificates.add(certs[i]); if ( verifyCanceled ) return null; } } else { // The entry is not signed if ( !je.isDirectory() && !name.toUpperCase().startsWith( "META-INF/" ) ) { // NOI18N anyUnsigned = true; } } } } if ( certificates.size() > 1 ) { throw new SecurityException( bundle.getString( "EXC_TooManySignatures" ) ); } if ( anySigned && anyUnsigned ) { throw new SecurityException( bundle.getString( "EXC_NotSignedEntity" ) ); } progressDialog.setGaugeValue( ProgressDialog.PARTIAL_GAUGE, flen / 1024 + 10); return anySigned ? certificates : null; } public static String formatCerts(Collection collection) { StringBuffer sb = new StringBuffer( collection.size() * 300 ); Iterator it = collection.iterator(); while(it.hasNext()) { Certificate cert = (Certificate)it.next(); if ( cert instanceof X509Certificate ) { try { sb.append( "\n\n" ); // NOI18N sb.append( X509CertToString( (X509Certificate) cert ) ); } catch ( Exception e ) { sb.append( cert.toString() ); } } else { sb.append( cert.toString() ); } sb.append( "\n\n" ); // NOI18N } return sb.toString(); } /** Tests whether the cets are trusted */ boolean isTrusted( Collection certs ) { Collection trustedCerts = getTrustedCerts(); if ( trustedCerts.size() <= 0 || certs.size() <= 0 ) return false; return trustedCerts.containsAll( certs ); } /** Adds certificates into keystore */ static void addCertificates( Collection certs ) throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException { KeyStore ks = getKeyStore( Autoupdater.Support.getUserKSFile(), KS_PSSWD, null); Iterator it = certs.iterator(); while ( it.hasNext() ) { Certificate c = (Certificate) it.next(); // don't add certificate twice if ( ks.getCertificateAlias( c ) != null ) continue; // Find free alias name String alias = null; for ( int i = 0; i < 9999; i++ ) { alias = "genAlias" + i; // NOI18N if ( !ks.containsAlias( alias ) ) break; } if ( alias == null ) throw new KeyStoreException( bundle.getString( "EXC_TooManyCertificates" ) ); ks.setCertificateEntry( alias, c ) ; } saveKeyStore( ks, Autoupdater.Support.getUserKSFile(), KS_PSSWD, null); } /** Removes certificates from the keystore */ static void removeCertificates( Collection certs ) throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException { KeyStore ks = getKeyStore( Autoupdater.Support.getUserKSFile(), KS_PSSWD, null); Iterator it = certs.iterator(); while ( it.hasNext() ) { Certificate c = (Certificate) it.next(); String alias = ks.getCertificateAlias( c ); if ( alias != null ) ks.deleteEntry( alias ); } saveKeyStore( ks, Autoupdater.Support.getUserKSFile(), KS_PSSWD, null); } // Keystore utility methods -------------------------------------------------------- /** Gets all trusted certificates */ Collection getTrustedCerts() { Collection trustedCerts = new ArrayList( 10 ); File cKS = Autoupdater.Support.getCentralKSFile(); File uKS = Autoupdater.Support.getUserKSFile(); try { if ( cKS.canRead() ) { KeyStore ks = getKeyStore( cKS, KS_PSSWD, null ); trustedCerts.addAll ( getCertificates( ks ) ); } if ( uKS.canRead() && !uKS.equals( cKS ) ) { KeyStore ks = getKeyStore( uKS, KS_PSSWD, null ); trustedCerts.addAll ( getCertificates( ks ) ); } } // In case of exception let the collection empty catch ( CertificateException e ) { } catch ( KeyStoreException e ) { } catch ( IOException e ) { } catch ( NoSuchAlgorithmException e ) { } return trustedCerts; } /** Creates keystore and loads data from file. * @param filename - name of the keystore * @param storetype - type of the keystore * @param password */ private static KeyStore getKeyStore(File file, String password, String storetype) throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException { InputStream is = null; try { is = new FileInputStream( file ); } catch ( IOException e ) { // Do nothing leaving is null creates empty key store } KeyStore keyStore = null; if ( storetype == null ) { keyStore = KeyStore.getInstance( KeyStore.getDefaultType() ); keyStore.load( is, password.toCharArray() ); if ( is != null ) is.close(); } return keyStore; } /** Creates keystore and loads data from file. * @param keyStore - keystore * @param filename - name of the keystore * @param storetype - type of the keystore * @param password */ public static void saveKeyStore(KeyStore keyStore, File file, String password, String storetype) throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException { OutputStream os = new FileOutputStream( file ); keyStore.store(os, password.toCharArray( ) ); os.close(); } /** Return all certificates in system ( Form central and user's directory ) */ public static Collection getCertificates(KeyStore keyStore) throws KeyStoreException { List certificates = new ArrayList( 10 ); Enumeration en = keyStore.aliases(); while (en.hasMoreElements()) { String alias = (String)en.nextElement(); Certificate cert = keyStore.getCertificate(alias); certificates.add(cert); } return certificates; } void cancelVerify() { verifyCanceled = true; } /** Prints a X509 certificate in a human readable format. */ private static String X509CertToString(X509Certificate cert ) throws Exception { return bundle.getString("MSG_Owner") + SPACE + (cert.getSubjectDN()) + NEW_LINE + bundle.getString("MSG_Issuer") + SPACE + (cert.getIssuerDN()) + NEW_LINE + bundle.getString("MSG_SerNumber") + SPACE + cert.getSerialNumber().toString(16) + NEW_LINE + bundle.getString("MSG_Valid") + SPACE + cert.getNotBefore().toString() + SPACE + bundle.getString("MSG_Until") + SPACE + cert.getNotAfter().toString() + NEW_LINE + bundle.getString("MSG_CertFinger") + NEW_LINE + SPACE + TAB + bundle.getString("MSG_MD5") + SPACE + SPACE + getCertFingerPrint("MD5", cert) + NEW_LINE + SPACE + TAB + bundle.getString("MSG_SHA1") + SPACE + getCertFingerPrint("SHA1", cert); } /** Gets the requested finger print of the certificate. */ private static String getCertFingerPrint(String mdAlg, Certificate cert) throws Exception { byte[] encCertInfo = cert.getEncoded(); MessageDigest md = MessageDigest.getInstance(mdAlg); byte[] digest = md.digest(encCertInfo); return toHexString(digest); } /**Converts a byte array to hex string */ private static String toHexString(byte[] block) { StringBuffer buf = new StringBuffer(); int len = block.length; for (int i = 0; i < len; i++) { byte2hex(block[i], buf); if (i < len-1) { buf.append(":"); // NOI18N } } return buf.toString(); } /** Converts a byte to hex digit and writes to the supplied buffer */ private static void byte2hex(byte b, StringBuffer buf) { char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; int high = ((b & 0xf0) >> 4); int low = (b & 0x0f); buf.append(hexChars[high]); buf.append(hexChars[low]); } } /* * Log * 8 Gandalf 1.7 1/18/00 Petr Hrebejk Keystore for multiuser * fixed * 7 Gandalf 1.6 1/13/00 Petr Hrebejk i18 mk3 * 6 Gandalf 1.5 1/12/00 Petr Hrebejk i18n * 5 Gandalf 1.4 12/20/99 Petr Hrebejk Autocheck & security * finished * 4 Gandalf 1.3 12/16/99 Petr Hrebejk Sign checking added * 3 Gandalf 1.2 12/1/99 Petr Hrebejk Checkin signatures of * NBM files & automatic autoupdate check added * 2 Gandalf 1.1 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 1 Gandalf 1.0 10/10/99 Petr Hrebejk * $ */